package com.buddy.design.pattern._03proxy;

import com.buddy.design.pattern._03proxy.cglib.LiSi;
import com.buddy.design.pattern._03proxy.cglib.MeiPo;
import com.buddy.design.pattern._03proxy.jdk.Meipo;
import com.buddy.design.pattern._03proxy.jdk.Zhangsan;
import com.buddy.design.pattern._03proxy.self.SMeipo;
import net.sf.cglib.core.DebuggingClassWriter;
import org.junit.Test;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

public class ProxyPattern {
    /*
     * 代理模式：为其他对象提供一个代理，以控制对这个对象的访问
     * 代理对象在目标对象和客户端之前起到中介的作用
     * 属于结构型设计模式
     * 适用场景：
     *  保护目标对象；
     *  增强目标对象
     * 静态代理
     *      显示的声明被代理对象
     * 动态代理(JDK)
     *      1.拿到被代理对象的引用，通过反射获取它的所有接口
     *      2.JDK Proxy重新生成一个类，实现被代理类所有接口和方法；
     *      3.动态生成Java代码，把增强逻辑加入到新代码中；
     *      4.编译生成class文件；
     *      5.加载并运行class
     *  CGLIB
     *
     *
     *  JDK采用读取接口信息；
     *  CGLIB采用继承父类；
     *  目的都是生成一个新的类，去实现增强代码逻辑的功能
     *
     *  JDK对用户而言必须要有一个接口实现，目标类相对来说比较复杂；
     *  CGLIB可以代理任意一个普通的类，没有任何要求；
     *
     *  CGLIB生成代理的逻辑更复杂效率低，但是调用效率更高，因为生成了一个包含了所有逻辑的FastClass，不再需要反射调用；
     *  JDK代理生成代理的逻辑简单，但执行效率相对要低，因为每次都要反射动态调用
     *
     *  CGLIB不能代理final的方法（继承）
     *
     */

    @Test
    public void personTest() {
        /**
         * 代理对象的实现原理：
         *  1.拿到被代理对象的引用；
         *  2.JDK重新生成一个类，实现代理对象所实现的接口；
         *  3.把被代理对象的引用也拿到了
         *  4.重新生成一个class字节码
         *  5.然后编译
         */
        Person person = (Person) new Meipo().getInstance(new Zhangsan());
        System.out.println(person.getClass());
        person.findLove();

        //获取对象字节码
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy4", new Class[]{person.getClass()});
        try (FileOutputStream fos = new FileOutputStream("$Proxy4.class");) {
            fos.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void sPersonTest() {
        Person person = (Person) new SMeipo().getInstance(new Zhangsan());
        System.out.println(person.getClass());
        person.findLove();
    }

    @Test
    public void cgLibTest() {
        //获取对象字节码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\GitProgect\\buddy-learn\\design-pattern\\cglib_classes");
        LiSi obj = (LiSi) new MeiPo().getInstance(new LiSi());
        obj.findLove();
        System.out.println(obj.getClass());
    }
}
